package com.lzh.yupao.service.impl;

import java.util.Date;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lzh.yupao.common.ErrorCode;
import com.lzh.yupao.exception.BusinessException;
import com.lzh.yupao.mapper.TeamMapper;
import com.lzh.yupao.model.domain.Team;
import com.lzh.yupao.model.domain.User;
import com.lzh.yupao.model.domain.UserTeam;
import com.lzh.yupao.model.dto.TeamQuery;
import com.lzh.yupao.model.enums.TeamStatusEnum;
import com.lzh.yupao.model.request.DeleteRequest;
import com.lzh.yupao.model.request.TeamJoinRequest;
import com.lzh.yupao.model.request.TeamQuitRequest;
import com.lzh.yupao.model.request.TeamUpdateRequest;
import com.lzh.yupao.model.vo.TeamUserVO;
import com.lzh.yupao.model.vo.UserVO;
import com.lzh.yupao.service.TeamService;
import com.lzh.yupao.service.UserService;
import com.lzh.yupao.service.UserTeamService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 队伍(Team)表服务实现类
 *
 * @author makejava
 * @since 2023-05-17 15:15:07
 */
@Service("teamService")
public class TeamServiceImpl extends ServiceImpl<TeamMapper, Team> implements TeamService {

    @Resource
    private UserTeamService userTeamService;

    @Resource
    private UserService userService;

    @Resource
    private RedissonClient redissonClient;


    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long addTeam(Team team, User loginUser) {
        //    1. 请求参数是否为空
        if (Objects.isNull(team)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //    2. 是否登陆，未登录不允许创建
        if (Objects.isNull(loginUser)) {
            throw new BusinessException(ErrorCode.NOT_LOGIN);
        }
        final Long userId = loginUser.getId();
        Integer maxNum = Optional.ofNullable(team.getMaxNum()).orElse(0);
        if (maxNum < 1 || maxNum > 20) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍人数不满足要求");
        }
        String name = team.getName();
        if (StringUtils.isBlank(name) || name.length() > 20) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍标题不满足要求");
        }
        String description = team.getDescription();
        if (StringUtils.isNotBlank(name) && description.length() > 512) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍描述过长");
        }
        Integer status = Optional.ofNullable(team.getStatus()).orElse(0);
        TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(status);

        if (statusEnum == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍状态不满足要求");
        }

        String password = team.getPassword();
        if (statusEnum.equals(TeamStatusEnum.SECRET)) {
            if (StringUtils.isBlank(password) || password.length() > 32) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码设置不正确");
            }
        }

        Date expireTime = team.getExpireTime();
        if (new Date().after(expireTime)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "超时时间 > 当前时间");
        }

        // todo 可能同时创建100个队伍
        RLock lock = redissonClient.getLock("yupao:create:team:user:" + userId);

        Long teamId = null;
        try {
            lock.tryLock(0, -1, TimeUnit.MILLISECONDS);
            LambdaQueryWrapper<Team> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Team::getUserId, userId);
            long hasTeamNum = count(queryWrapper);
            if (hasTeamNum >= 5) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户最多创建5个队伍");
            }

            team.setId(null);
            team.setUserId(userId);
            // 插入队伍信息到队伍表
            boolean result = this.save(team);
            teamId = team.getId();
            if (!result || teamId == null) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "创建队伍失败");
            }

            // 插入用户 => 队伍关系到关系表
            UserTeam userTeam = new UserTeam();
            userTeam.setUserId(userId);
            userTeam.setTeamId(teamId);
            userTeam.setJoinTime(new Date());
            result = userTeamService.save(userTeam);
            if (!result) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "创建队伍失败");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }

        return teamId;
    }

    @Override
    public List<TeamUserVO> listTeams(TeamQuery teamQuery, boolean isAdmin) {
        LambdaQueryWrapper<Team> queryWrapper = new LambdaQueryWrapper<>();

        // 组合查询条件
        if (teamQuery != null) {
            Long id = teamQuery.getId();
            queryWrapper.eq(id != null && id > 0, Team::getId, id);

            List<Long> idList = teamQuery.getIdList();
            queryWrapper.in(CollectionUtils.isNotEmpty(idList), Team::getId, idList);

            String name = teamQuery.getName();
            queryWrapper.like(StringUtils.isNotBlank(name), Team::getName, name);

            String description = teamQuery.getDescription();
            queryWrapper.like(StringUtils.isNotBlank(description), Team::getDescription, description);

            String searchText = teamQuery.getSearchText();
            if (StringUtils.isNotBlank(searchText)) {
                queryWrapper.and(qw -> qw.like(Team::getName, searchText).or().like(Team::getDescription, searchText));
            }
            // 查询最大人数相等的
            Integer maxNum = teamQuery.getMaxNum();
            queryWrapper.eq(maxNum != null && maxNum > 0, Team::getMaxNum, maxNum);

            // 根据创建人查询
            Long userId = teamQuery.getUserId();
            queryWrapper.eq(userId != null && userId > 0, Team::getUserId, userId);

            Integer status = teamQuery.getStatus();
            TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(status);
            if (statusEnum == null) {
                statusEnum = TeamStatusEnum.PUBLIC;
            }
            if (!isAdmin && statusEnum.equals(TeamStatusEnum.PRIVATE)) {
                throw new BusinessException(ErrorCode.NO_AUTH);
            }
            queryWrapper.eq(Team::getStatus, statusEnum.getValue());
        }
        // 不展示已过期的队伍
        queryWrapper.and(qw -> qw.gt(Team::getExpireTime, new Date()).or().isNull(Team::getExpireTime));

        List<Team> teamList = this.list(queryWrapper);
        if (CollectionUtils.isEmpty(teamList)) {
            return new ArrayList<>();
        }
        // 关联查询用户信息
        // 1.SQL
        // 查询队伍和创建人的信息
        // select * from team t left join user u on t.userId = u.id
        // 查询队伍和已加入队伍成员的信息
        // select * from team t join user_team ut on t.id = ut.teamId

        // 关联查询创建人的用户信息
        List<TeamUserVO> teamUserVOList = new ArrayList<>();
        for (Team team : teamList) {
            Long userId = team.getUserId();
            if (userId == null) {
                continue;
            }
            User user = userService.getById(userId);
            // 脱敏用户信息
            TeamUserVO teamUserVO = new TeamUserVO();
            BeanUtils.copyProperties(team, teamUserVO);
            if (user != null) {
                UserVO userVO = new UserVO();
                BeanUtils.copyProperties(user, userVO);
                teamUserVO.setCreateUser(userVO);
            }

            teamUserVOList.add(teamUserVO);
        }
        return teamUserVOList;
    }

    @Override
    public boolean updateTeam(TeamUpdateRequest teamUpdateRequest, User loginUser) {
        if (Objects.isNull(teamUpdateRequest)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Long id = teamUpdateRequest.getId();
        if (id == null || id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Team oldTeam = this.getById(id);
        if (Objects.isNull(oldTeam)) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "数据不存在");
        }
        if (!oldTeam.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser)) {
            throw new BusinessException(ErrorCode.NO_AUTH);
        }
        TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(teamUpdateRequest.getStatus());
        if (statusEnum.equals(TeamStatusEnum.SECRET)) {
            if (StringUtils.isBlank(teamUpdateRequest.getPassword())) {
                if (StringUtils.isBlank(oldTeam.getPassword())) {
                    throw new BusinessException(ErrorCode.PARAMS_ERROR, "加密房间必须要有密码");
                }
            }
        }
        Team updateTeam = new Team();
        BeanUtils.copyProperties(teamUpdateRequest, updateTeam);
        return this.updateById(updateTeam);
    }

    @Override
    public boolean joinTeam(TeamJoinRequest teamJoinRequest, User loginUser) {
        if (teamJoinRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Long userId = loginUser.getId();

        Long teamId = teamJoinRequest.getTeamId();
        Team team = getTeamById(teamId);

        Date expireTime = team.getExpireTime();
        if (expireTime != null && new Date().after(expireTime)) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "队伍已过期");
        }
        Integer status = team.getStatus();
        TeamStatusEnum teamStatusEnum = TeamStatusEnum.getEnumByValue(status);
        if (teamStatusEnum.equals(TeamStatusEnum.PRIVATE)) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "禁止加入私有队伍");
        }
        String password = teamJoinRequest.getPassword();
        if (TeamStatusEnum.SECRET.equals(teamStatusEnum)) {
            if (StringUtils.isBlank(password) || !password.equals(team.getPassword())) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码错误");
            }
        }

        // 分布式锁
        RLock lock = redissonClient.getLock("yupao:join_team");

        try {
            while (true) {
                if (lock.tryLock(0, 30000L, TimeUnit.MILLISECONDS)) {
                    System.out.println("join team getLock: " + Thread.currentThread().getId());

                    // 当前用户已加入的队伍数
                    LambdaQueryWrapper<UserTeam> userTeamLambdaQueryWrapper = new LambdaQueryWrapper<>();
                    userTeamLambdaQueryWrapper.eq(UserTeam::getUserId, userId);
                    long hasJoinNum = userTeamService.count(userTeamLambdaQueryWrapper);
                    if (hasJoinNum > 5) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "最多加入5个队伍");
                    }
                    // 不能重复加入已加入的队伍
                    userTeamLambdaQueryWrapper = new LambdaQueryWrapper<>();
                    userTeamLambdaQueryWrapper.eq(UserTeam::getUserId, userId);
                    userTeamLambdaQueryWrapper.eq(UserTeam::getTeamId, teamId);
                    long hasUserJoinTeam = userTeamService.count(userTeamLambdaQueryWrapper);
                    if (hasUserJoinTeam > 0) {
                        throw new BusinessException(ErrorCode.NULL_ERROR, "用户已加入该队伍");
                    }
                    // 已加入当前队伍的人数
                    long teamHasJoinNum = countTeamUserByTeamId(teamId);
                    if (teamHasJoinNum >= team.getMaxNum()) {
                        throw new BusinessException(ErrorCode.NULL_ERROR, "队伍已满");
                    }
                    // 修改队伍人数信息
                    UserTeam userTeam = new UserTeam();
                    userTeam.setUserId(userId);
                    userTeam.setTeamId(teamId);
                    userTeam.setJoinTime(new Date());
                    return userTeamService.save(userTeam);
                }
            }

        } catch (InterruptedException e) {
            log.error("Join Team error", e);
            return false;
        } finally {
            // 只能释放自己的锁
            if (lock.isHeldByCurrentThread()) {
                System.out.println("join teamunLock: " + Thread.currentThread().getId());
                lock.unlock();
            }
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean quitTeam(TeamQuitRequest teamQuitRequest, User loginUser) {
        if (teamQuitRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Long teamId = teamQuitRequest.getTeamId();
        Team team = getTeamById(teamId);
        // 当前登陆用户id
        Long userId = loginUser.getId();

        UserTeam queryUserTeam = new UserTeam();
        queryUserTeam.setUserId(userId);
        queryUserTeam.setTeamId(teamId);
        LambdaQueryWrapper<UserTeam> userTeamLambdaQueryWrapper = new LambdaQueryWrapper<>(queryUserTeam);
        long count = userTeamService.count(userTeamLambdaQueryWrapper);
        if (count == 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "未加入队伍");
        }
        long teamHasJoinNum = countTeamUserByTeamId(teamId);
        // 队伍只剩一人
        if (teamHasJoinNum == 1) {
            // 删除队伍
            this.removeById(teamId);
            // 删除所有加入队伍的关系
            LambdaQueryWrapper<UserTeam> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(UserTeam::getTeamId, teamId);
            return userTeamService.remove(queryWrapper);
        } else {
            // 队伍至少还剩2人
            // 是否为队长
            if (team.getUserId().equals(userId)) { // 是队长
                // 把队伍转移给最早加入的用户
                // 1.查询已加入队伍的所有用户和加入时间
                LambdaQueryWrapper<UserTeam> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(UserTeam::getTeamId, teamId);
                queryWrapper.last("order by id asc limit 2");
                List<UserTeam> userTeamList = userTeamService.list(queryWrapper);
                if (CollectionUtils.isEmpty(userTeamList) || userTeamList.size() <= 1) {
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR);
                }
                // 获取下一个队长
                UserTeam nextUserTeam = userTeamList.get(1);
                Long nextTeamLeaderId = nextUserTeam.getUserId();
                // 更新队长
                Team updateTeam = new Team();
                updateTeam.setId(teamId);
                updateTeam.setUserId(nextTeamLeaderId);
                boolean result = this.updateById(updateTeam);
                if (!result) {
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR, "更新队长失败");
                }
            }
            // 删除当前退出队伍用户 加入队伍关系
            return userTeamService.remove(userTeamLambdaQueryWrapper);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean deleteTeam(DeleteRequest teamDeleteRequest, User loginUser) {
        //1. 校验请求参数

        //2. 校验队伍是否存在
        Team team = getTeamById(teamDeleteRequest.getId());
        Long teamId = team.getId();
        //3. 校验我是否为队伍的队长
        if (!team.getUserId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH, "无访问权限");
        }
        //4. 移除所有加入队伍的关联信息
        LambdaQueryWrapper<UserTeam> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserTeam::getTeamId, teamId);
        boolean result = userTeamService.remove(queryWrapper);
        if (!result) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "删除队伍关联信息失败");
        }
        //5. 删除队伍
        return this.removeById(teamId);
    }

    /**
     * 根据队伍id获取队伍信息
     *
     * @param teamId
     * @return
     */
    private Team getTeamById(Long teamId) {
        if (teamId == null || teamId <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Team team = this.getById(teamId);
        if (Objects.isNull(team)) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "队伍不存在");
        }
        return team;
    }


    /**
     * 获取某队伍当前人数
     *
     * @param teamId
     * @return
     */
    private long countTeamUserByTeamId(long teamId) {
        LambdaQueryWrapper<UserTeam> userTeamLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userTeamLambdaQueryWrapper.eq(UserTeam::getTeamId, teamId);
        return userTeamService.count(userTeamLambdaQueryWrapper);
    }

}